package demo2;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 姚东名
 * Date: 2024-06-08
 * Time: 9:42
 */
/*
【本章目标】
1.理解数组基本概念
2.掌握数组的基本用法
3.数组与方法互操作
4.熟练掌握数组相关的常见问题和代码
 */

/*
1.数组的基本概念

1.1为什么要有数组
假设现在要存5个学生的javaSE考试成绩，并对其进行输出，按照之前掌握的知识点，我么会写出如下代码：
 */
public class Test {
    public static void main(String[] args) {
        /*int[] array = null;
        System.out.println(array[0]);*/
        //AA aa = new AA();
        AA.function();
        int score1 = 70;
        int score2 = 75;
        int score3 = 80;
        int score4 = 85;
        int score5 = 90;

        System.out.println(score1);
        System.out.println(score2);
        System.out.println(score3);
        System.out.println(score4);
        System.out.println(score5);

        int[] array3;
        array3 = new int[]{1, 2, 3, 4, 5};

        int[] array4 = {1, 2, 3, 4, 5};

        /*int[] array = {1, 2, 3};
        System.out.println(array[3]);*/

        int[] array = {1, 2, 3, 4, 5};
        for (int i = 0; i < array.length; i++) {
            System.out.println(array[i]);
        }
    }
}

/*
上述的代码没有任何的问题，但是不好的是：如果有20名同学成绩呢，需要创建20个变量吗？有100个呢？仔细观察这些学生成绩发现：所有成绩的类型都是相同的，那Java中存在可以储存相同类型多个数据的类型吗？就是数组。

1.2什么是数组
数组：可以看成是相同类型元素的一个集合。在内存中是一段连续的空间。比如现实的车库：

在Java中，包含6个整型元素的数组，相当于上图中的连在一起的6个车位，从上图中可以看到：
1.数组中存放的元素其类型相同
2.数组的空间是连在一起的
3.每个空间有自己的编号，其实位置的编号为0，即数组的下标。

1.3数组的创建及初始化

1.3.1数组的创建
T[] 数组名 = new T[N];
T：代表数组存放元素的类型
T[]：表示数组的类型
N：表示数组的长度

int[] array1 = new int[10];//创建一个可以容纳10个int类型元素的数组
double[] array2 = new double[5];//创建一个可以容纳5个double类型元素的数组
String[] array3 = new String[3];//创建一个可以容纳3个String类型元素的数组

1.3.2数组的初始化
数组的初始化主要分为动态初始化以及静态初始化。
  1.动态初始化：在创建数组是，直接指定数组中元素的个数
  int[] array = new int[10];
  2.静态初始化：在创建数组时不直接指定数据元素个数，而直接将具体的数据内容进行指定
  语法格式：T[]数组名称 = {data1, data2, data3, datan};

  int[] array1 = new int[]{0, 1, 2, 3, 4, 5};
  double[] array2 = new double[]{1.2, 3.2, 4.5, 5.6};
  String[] array3 = new String[]{"Hello", "Java", "!!"};

  【注意事项】
  - 静态初始化虽然没有指定数组的长度，编译器在编译时会根据{}中的元素个数来确定数组的长度。
  - 静态初始化时，{}中数据类型必须与[]前数据类型一致。
  - 静态初始化可以简写，省略后面的new T[]

   //注意：虽然很省略了new T[]，但是编译器编译代码时还是会还原
    int[] array1 = {0, 1, 2, 3, 4, 5};
    double[] array2 = {1.2, 3.2, 4.5, 5.6};
    String[] array3 = {"Hello", "Java", "!!"};

  -数组也可以按照如下C语言个数创建，不推荐
   该种定义方式不太友好，容易造成数组的类型就是int的误解
   []如果在类型之后，就表示数组类型，因此int[]结合在一起写就很清晰了
   int arr[] = {1, 2, 3};

 - 静态数组和动态数组初始化也可以分为两种，但是省略格式不可以
  int[] array1;
  array1 = new int[10];

  int[] array2;
  array2 = new int[]{1, 2, 3, 4};

  //注意省略格式不可以拆分，否则编译失败
  //int[] array3;
  array3 = {1, 2, 3, 4, 5};

 - 如果没有对数组进行初始化，数组中元素有其默认值
   - 如果数组中存储元素类型，默认值为基类类型对应的默认值，比如：
   - 如果数组中存储元素类型为引用类型，默认值为null

1.4数组的使用

1.4.1数组中元素访问

数组在内存中是连续存放的，空间的编号都是从0开始的，依次递增，该编号称为数组的下标，数组可以通过下标访问其任意位置的元素。比如：
   int[]array = new int[]{1, 2, 3, 4, 5};
   System.out.println(array[0]);
   System.out.println(array[1]);
   System.out.println(array[2]);
   System.out.println(array[3]);
   System.out.println(array[4]);

   //也可以通过[]对数组中的元素进行修改
   array[0] = 10;
   System.out.println(array[0]);

【注意事项】
1.数组是一段连续的内存空间，因此支持随机访问，即通过下标访问快速访问数组中任意位置的元素
2.下标从0开始，介于[0,N) 之间不包括N，N为元素个数，不能越界，否则会报出下标越界异常。

  int[] array = {1, 2, 3};
  System.out.println(array[3]);//数组中只有3个元素，下标一定是：0 1 2，array[3]访问下标3，越界了

  //执行结果
    Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
	at demo2.Test.main(Test.java:44)

	抛出java.lang.ArrayIndexOutOfBoundsException异常，使用数组一定要下标谨防下标越界

	int[]array = new int[]{1, 2, 3, 4, 5};
	System.out.println(array[0]);
	System.out.println(array[1]);
	System.out.println(array[2]);
	System.out.println(array[3]);
	System.out.println(array[4]);

1.4.2遍历数组
所谓“遍历”是指将数组中的所有元素都访问一遍，访问是指对数组中的元素进行某种操作，比如：打印
int[]array = new int[]{1, 2, 3, 4, 5};
System.out.println(array[0]);
System.out.println(array[1]);
System.out.println(array[2]);
System.out.println(array[3]);
System.out.println(array[4]);


    上述代码中可以起到对数组中元素遍历的目的，但是问题是：
    1.如果数组中增加了一个元素，就需要增加一条打印语句
    2.如果输入中有100个元素，就需要写一个100个打印语句
    3.如果现在要把打印修改为给数组中每个元素加1，修改起来非常麻烦

  通过观察代码可以发现，对数组中每个元素的操作都是相同的，则可以使用循环来进行打印

int[] array = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
    System.out.println(array[i]);
}
  改为循环之后，上述代码可以完全将2和3问题全部解决，但是无法解决问题1，那能否获取到数组的长度呢？
  注意：在数组中可以通过，数组对象.length来获取数组的长度
int[] array = {1, 2, 3, 4, 5};
for (int i = 0; i < array.length; i++) {
    System.out.println(array[i]);
}

也可以使用for-each 遍历数组
int[]array = {1, 2, 3};
for(int x : array) {
  System.out.println(x);
}

for-each是for循环的另外一种使用方式，能够更方便的完成对数组的遍历，可以避免循环条件和更新语句写错


2.数组是引用类型

2.1初识JVM(Java Virtual Machine-Java虚拟机)

内存是一段连续的存储空间，主要用来存储程序运行时数据的。比如：
 1.程序运行时代码需要加载到内存
 2.程序运行产生的中间数据要存放在内存
 3.程序中的常量也要保存
 4.有些数据可能需要长时间存储，而有些数据当方法运行结束后就要被销毁

如果对内存中存储的数据不加以区分的随意存储，那对内存管理起来将会非常困难。比如：

因此JVM也对所使用的内存按照功能的不同进行了划分：

- 程序计数器(PC Register)：只是一个很小的空间，保存下一条执行的指令的地址
- 虚拟机栈(JVM Stack)：与方法调用相关的一些信息，每个方法在执行时，都会先创建一个栈帧，栈帧中包含有：局部变量表、操作数栈、动态链接、返回地址以及一些其他的信息，保存的都是于方法执行是相关的一些信息。比如：局部变量。当方法运行结束后，栈帧就被销毁了，即保存在栈帧中的数据也被销毁了。
- 本地方法栈(Native Method Stack)：本地方法栈于虚拟机栈的作用类似，只不过保存的内容是Native方法的局部变量。在有些版本的JVM实现中(例如HotSpot)，本地方法栈和虚拟机是在一起的
- 堆(Heap)：JVM所管理的最大内存区域，使用new 创建的对象都是在堆上保存(例如前面的 new int[]{1, 2, 3, 4, 5})，堆是随着程序开始运行时而创建，随着程序的退出而销毁，堆中的数据只要还有在使用，就不会被销毁。
- 方法区(Method Area)：用于存储已被虚拟机加载的类信息、常量、静态常量、即编译器编译后的代码等数据，方法编译出的字节码就是保存在这个区域的

现在只关心堆和虚拟机栈这两块空间，后续JVM中还会详细介绍。

2.2基本类型变量与引用变量的区别
基本数据创建的变量，称为基本变量，该变量空间中直接存放的是是其对应的值；
而引用数据创建的变量，一般称为对象的引用，其空间中存储的对象所在的地址。

public static void func() {
  int a = 10;
  int b = 20;
  int[] array = new int[]{1, 2, 3};
}

在上述代码中，a、b、array，都是函数内部的变量，因此其空间都在main方法对应的栈帧中分配
a、b是内置类型的数据变量，因此其空间中保存的就是给该变量初始化的值。
array是数组类型的引用变量，其实内存的内存可以简单理解为是数组在维空间中的首地址。

从图可以看出，引用变量并不直接存储对象本身，可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址，引用变量便可以去操作对象了。有些类似C语言中的指针，但是Java中的引用要比指针的操作更加简单。
*/

class AA {
    //2.3再谈引用变量
    public static void function() {
        int[] array1 = new int[3];
        array1[0] = 10;
        array1[1] = 20;
        array1[2] = 30;

        int[] array2 = new int[]{1, 2, 3, 4, 5};
        array2[0] = 100;
        array2[1] = 200;

        array1 = array2;
        array1[2] = 300;
        array1[3] = 400;
        array2[4] = 500;

        for (int i = 0; i < array2.length; i++) {
            System.out.println(array2[i]);
        }
    }
}
/*
2.4认识null
null在Java中表示“空引用”，也就是一个不指向对象的引用

int[] array = null;
System.out.println(array[0]);

//执行结果
Exception in thread "main" java.lang.NullPointerException
	at demo2.Test.main(Test.java:27)

null的作用类似与C语言中的NULL(空指针)，都是表示一个无效的内存位置。因此不能对这个内存进行任何读写操作，一旦尝试读写，就会抛出NullPointerException(空指针异常)
注意：Java中并没有约定null和0号地址的内存有任何关联
 */

